#include "profiler.h"
#include "mmgr.h"		// Needs to come in early
#include "timer.h"
#include <cassert>

using namespace util;

Profiler&           Profiler::Instance()
{
    static  Profiler    Inst;
    return Inst;
}

Profiler::Profiler() :
	m_IsEnabled(false),
	m_LastTotalTime(0)
{
    Reset();
//    CalculateTicksSecond();
	CreateSection("Rendering");
	CreateSection("Network");
	CreateSection("Scripts");
	CreateSection("UI");
}

Profiler::~Profiler()
{
}

void                Profiler::StartFrame()
{
	if (!m_IsEnabled) return;
	m_Groups.ForEach(ProfilingGroup::ResetCB, NULL);
    m_FrameStart  = Timer::GetTimeStamp();
}


void                Profiler::EndFrame()
{
	if (!m_IsEnabled) return;
    // automatically end all open timers
	for (unsigned int i=0; i<m_Groups.GetElementC(); i++)
    {
		ProfilingGroup* pGroup = m_Groups.GetElementByIndex(i);

		//StopSection(pGroup->Name); // close any open timers

        // Should we store a history of instructions ?
		pGroup->LastFrame = pGroup->Accumulated;
        // Make sure the accumulator is reset as well
		pGroup->Accumulated  = 0;
    }

	float    FrameEnd = Timer::GetTimeStamp();
	m_LastTotalTime = FrameEnd - m_FrameStart;
}

void                Profiler::Reset()
{
	m_Groups.ForEach(ProfilingGroup::ResetCB, NULL);
    m_FrameStart			= 0;

	//reset data
}


void                Profiler::StartSection(const std::string& Name)
{
	if (!m_IsEnabled) return;

	if (ProfilingGroup*	pGroup =GetSection(Name))
	{
		if (pGroup->StartTime == 0)       // not currently active
		{
			pGroup->StartTime = Timer::GetTimeStamp();
		}
	}
}

void                Profiler::StopSection(const std::string& Name)
{
	if (!m_IsEnabled) return;
    float Delta = Timer::GetTimeStamp();                       
	if (ProfilingGroup*	pGroup = GetSection(Name) )
	{
		if (pGroup->StartTime > 0)
		{
			Delta = Delta - pGroup->StartTime;
			pGroup->Accumulated += Delta;
			pGroup->StartTime = 0;        // stop timer afterwards
		}
	}
}

float   Profiler::GetSectionTime(const std::string& Name)
{
	if (ProfilingGroup*	pGroup =GetSection(Name))
	{
		return (pGroup->LastFrame);
	}
	return 0.0f;
}


float               Profiler::GetSectionPercentage(const std::string& Name)
{
	if (ProfilingGroup*	pGroup =GetSection(Name))
	{
		float TicksInSection = pGroup->LastFrame;
		float TicksTotal     = m_LastTotalTime;
		return (float)( (TicksInSection / TicksTotal) * 100.0);
	}
	return 0;
}

void                Profiler::AddSectionTicks(const std::string& Name, float _Time)
{   
	if (ProfilingGroup*	pGroup =GetSection(Name))
	{
	    pGroup->Accumulated += _Time;
	} else
	{
		pGroup = CreateSection(Name);
		pGroup->Accumulated += _Time;
	}
}


ProfilingGroup*	Profiler::CreateSection(const std::string& Name)
{
	ProfilingGroup*	pGroup = GetSection(Name);
	if (pGroup ) return pGroup;	// already exists
	pGroup = new ProfilingGroup;
	pGroup->Name = Name;
	m_Groups.AddElement(Name, pGroup);
	return pGroup;
}
